<template>
<div
 class="vk-tooltip"
 v-on="outerEvents"
 ref="popperContainerNode"
 >
  <!-- 触发区 -->
  <div
    class="vk-tooltip__trigger"
    ref="triggerNode"
    v-on="events"
  >
    <slot></slot>
  </div>
  <!-- 展示区 -->
  <Transition :name="transition">
     <div
       v-if="isOpen"
       class="vk-tooltip__popper"
       ref="popperNode"
     >
        <slot name="content">
         {{ content }}
        </slot>
        <div id="arrow" data-popper-arrow></div>
     </div>
  </Transition>

</div>
</template>



<script setup lang="ts">
import { ref,watch,reactive, onUnmounted,computed} from 'vue'
import {createPopper} from '@popperjs/core'
import type {Instance} from '@popperjs/core'
import type { TooltipProps, TooltipEmits,TooltipInstance } from './types.ts'
import useClickOutside from '../../hooks/useClickOutside'
//相当于一个延时加防抖
import { debounce }  from 'lodash-es'
defineOptions({
  name:'VkTooltip'
})
const props = withDefaults(defineProps<TooltipProps>(),{
  placement: 'bottom',
  trigger:'hover',
  transition:'fade',
  openDelay: 0,
  closeDelay: 0,
})
const emits = defineEmits<TooltipEmits>()
const isOpen = ref(false)
const popperNode = ref<HTMLElement>()
const triggerNode = ref<HTMLElement>()
const popperContainerNode = ref<HTMLElement>()
let popperInstance : null | Instance = null


//动态事件绑定

type EventHandler = () => void;
// 定义事件对象的类型
interface EventMap {
  [key: string]: EventHandler;
}
let events:EventMap = reactive({})
let outerEvents:EventMap = reactive({})
let openTimes = 0
let closeTimes = 0
const popperOptions=computed(()=>{
  return{
    placement: props.placement,
    modifiers:[{
      name: 'offset',
      options:{
      offset:[0,9],
      },
    }],
    ...props.popperOptions
  }
})

const open=()=>{
    openTimes++
    console.log('open times',openTimes)
    isOpen.value = true
    emits('visible-change',isOpen.value)
}
const close=()=>{
  closeTimes++
  console.log('close times',closeTimes)
    isOpen.value = false
    emits('visible-change',isOpen.value)
}
const openDebounce=debounce(open,props.openDelay)
const closeDebounce=debounce(close,props.closeDelay)
const openFinal=()=>{
  closeDebounce.cancel()
  openDebounce()
}
const closeFinal=()=>{
  openDebounce.cancel()
  closeDebounce()
}
const togglePopper = () => {
  if (isOpen.value) {
    closeFinal()
  } else {
    openFinal()
  }
}

useClickOutside(popperContainerNode, () => {
  if(props.trigger==='click'&& isOpen.value&& !props.manual){
    closeFinal()
  }
  if(isOpen.value){
    emits('click-outside',true)
  }
})
const attachEvents = ()=>{
 if(props.trigger==='hover'){
    events['mouseenter'] = openFinal
    //放在父节点，保证鼠标移开触发区，但没有移开展示区时，展示区不消失
    outerEvents['mouseleave'] =closeFinal  
 }else if(props.trigger==='click'){
   events['click'] = togglePopper
 }
}
if(!props.manual){
  attachEvents()
}
// 监听手动触发的变化，如果手动触发，事件就不绑定了，如果不是手动触发，就绑定事件
watch(()=>props.manual,(isManual)=>{
  if(isManual){
    events = {}
    outerEvents = {}
  }else{
    attachEvents()
  }
})
// 监听trigger的变化（比如用计时器两秒之后修改trigger），重新绑定事件,所以事件要用reactive设置为响应式的
watch(()=>props.trigger,(newTrigger,oldTrigger)=>{
  if (oldTrigger!== newTrigger) {
    events={}
    outerEvents={} 
    attachEvents();
    
  }
})


// dom节点生成后：{flush: 'post'}，显示的时候创建实例不显示时销毁实例
watch(isOpen,(newValue) => {
  if (newValue) {
     if (triggerNode.value && popperNode.value) {
      //@popperjs/core 是一个强大的库，用于动态计算和更新弹出框的位置。
      //在createPopper中传入触发区和展示区的DOM节点，并指定 placement（如 'top', 'bottom', 'left', 'right'）来控制弹出框的显示位置
       popperInstance = createPopper (triggerNode.value, popperNode.value,popperOptions.value)
     }else{
      popperInstance?.destroy()
     }
    }
  },{flush: 'post'})
  
  onUnmounted(()=>{
    popperInstance?.destroy()
  })

  defineExpose<TooltipInstance>({
    'show':openFinal,
    'hide':closeFinal
  })
</script>